﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using JasLib.Extensions;
using JasLib.Interop.Win32;

namespace JasLib.Interop
{
    public static class MarshalHelper
    {
        /// <summary>
        /// We have the liberty of using a cache because the marshal size of a type is constant after startup.
        /// </summary>
        private static Dictionary<Type, UINT_PTR> s_marshalSizeOfCache = new Dictionary<Type, UINT_PTR>();

        /***************************************************************************/
        private static UINT_PTR PrivateSizeOf(Type type)
        {
            UINT_PTR uiSizeOf;
            lock (s_marshalSizeOfCache)
            {
                if (!s_marshalSizeOfCache.TryGetValue(type, out uiSizeOf))
                {
                    /// Custom fixups.
                    /// http://blogs.msdn.com/b/hongmeig/archive/2004/08/19/216940.aspx
                    if (type == typeof(char))
                        uiSizeOf = 2;
                    else if (type == typeof(DateTime))
                        uiSizeOf = SizeOf<long>();
                    else if (type.IsType(typeof(System.Enum)))
                        uiSizeOf = Marshal.SizeOf(Enum.GetUnderlyingType(type));
                    else
                        uiSizeOf = Marshal.SizeOf(type);
                    s_marshalSizeOfCache.Add(type, uiSizeOf);
                }
            }
            return uiSizeOf;
        }

        /***************************************************************************/
        /// <summary>
        /// This function is about 7% faster than Marshal.SizeOf() when called repeatedly on the same type,
        /// plus it makes corrections for known false values and otherwise unsupported types.
        /// It also supports enums.
        /// </summary>
        public static UINT_PTR SizeOf<T>()
        {
            return PrivateSizeOf(typeof(T));
        }

        /***************************************************************************/
        public static UINT_PTR SizeOf<T>(T target)
        {
            return PrivateSizeOf(typeof(T));
        }

        /***************************************************************************/
        public static T[] PtrArrayToStructureArray<T>(IList<UINT_PTR> pointers) where T : struct
        {
            T[] structArray = new T[pointers.Count];

            for (int iIndex = 0; iIndex < pointers.Count; iIndex++)
            {
                structArray[iIndex] = PtrToStructure<T>(pointers[iIndex]);
            }

            return structArray;
        }

        /***************************************************************************/
        /// <summary>
        /// Wraps Marshal.PtrToStructure without repetitive type syntax.
        /// </summary>
        public static T PtrToStructure<T>(UINT_PTR pointer) where T : struct
        {
            return (T)Marshal.PtrToStructure(pointer, typeof(T));
        }

        /***************************************************************************/
        public static T[] PtrToStructureArray<T>(UINT_PTR pointer, long lCount) where T : struct
        {
            int iStructSize = SizeOf<T>();

            T[] structures = new T[lCount];
            UINT_PTR pNext = pointer;
            for (long lIndex = 0; lIndex < lCount; lIndex++)
            {
                structures[lIndex] = PtrToStructure<T>(pNext);
                pNext += iStructSize;
            }

            return structures;
        }

        /***************************************************************************/
        public static UINT_PTR[] ToIntPtrArray(this SafeHandle[] handles)
        {
            return handles.ConvertAll(t => (UINT_PTR)t.DangerousGetHandle());
        }
    }
}